// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// IR访客可以在IR树上行走。
// None
// 最低级别的帮助者是DoChildren和EditChildren，它们
// 节点有助于实现并提供对是否以及何时的控制
// 递归发生在IR的遍历过程中。
// None
// 尽管这两种模式都直接有用，但有两种更简单的模式
// 是相当常见的，也提供：访问和任何。

package ir

// DoChildren在n的每个非零子节点x上调用do（x）。
// 如果任何调用返回true，则DoChildren停止并返回true。
// 否则，DoChildren返回false。
// None
// 注意，DoChildren（n，do）只为n的直系子调用do（x）。
// 如果应该处理x的子项，则do（x）必须调用DoChildren（x，do）。
// None
// DoChildren允许构造IR图的一般遍历
// 如果需要，可以提前停止。最普遍的用法是：
// None
// var do func（ir.Node）布尔
// do=func（x ir.Node）bool{
// ... 访问儿童前的处理。。。
// 如果我应该去看孩子。。。{
// 多克儿童（x，do）
// ... 访问儿童后处理。。。
// }
// 如果应该阻止父母子女打电话探望兄弟姐妹。。。{
// 返回真值
// }
// 返回错误
// }
// do（根）
// None
// 由于DoChildren本身不返回true，如果do函数
// 永远不想停止穿越，它可以假定多克儿童
// 其本身将始终返回false，简化为：
// None
// var do func（ir.Node）布尔
// do=func（x ir.Node）bool{
// ... 访问儿童前的处理。。。
// 如果我应该去看孩子。。。{
// 多克儿童（x，do）
// }
// ... 访问儿童后处理。。。
// 返回错误
// }
// do（根）
// None
// 访问功能进一步简化了模式，
// 仅在访问儿童之前进行处理，且从不停止：
// None
// 函数访问（n ir.Node，访问函数（ir.Node））{
// 如果n==nil{
// 回来
// }
// var do func（ir.Node）布尔
// do=func（x ir.Node）bool{
// 访问（x）
// 返回ir.Dochldren（x，do）
// }
// do（n）
// }
// None
// Any函数说明了模式的不同简化，
// 递归地访问每个节点及其子节点，直到找到
// cond（x）返回true的节点x，此时整个
// 遍历停止并返回true。
// None
// func Any（n ir.Node，cond（ir.Node）bool）bool{
// 如果n==nil{
// 返回错误
// }
// var do func（ir.Node）布尔
// do=func（x ir.Node）bool{
// 返回条件（x）| | ir.dochldens（x，do）
// }
// 返回do（n）
// }
// None
// 以上介绍了如何使用Visite和Any的示例
// 多克儿童有效，但当然，使用符合
// 通过访问或其他方式获取的简化将得到最好的服务
// 通过直接调用此包提供的。
func DoChildren(n Node, do func(Node) bool) bool {
	if n == nil {
		return false
	}
	return n.doChildren(do)
}

// Visit访问以n为根的IR树中的每个非nil节点x
// 在深度优先的前序遍历中，对所访问的每个节点调用visit。
func Visit(n Node, visit func(Node)) {
	if n == nil {
		return
	}
	var do func(Node) bool
	do = func(x Node) bool {
		visit(x)
		return DoChildren(x, do)
	}
	do(n)
}

// VisitList为列表中的每个节点x调用Visit（x，Visit）。
func VisitList(list Nodes, visit func(Node)) {
	for _, x := range list {
		Visit(x, visit)
	}
}

// Any在以n为根的IR树中查找非nil节点x
// 其中cond（x）返回true。
// 任何节点都以深度优先、前序遍历的方式考虑节点。
// 当Any找到一个节点x，使得cond（x）为true时，
// Any结束遍历并立即返回true。
// 否则，在完成整个遍历后，Any返回false。
func Any(n Node, cond func(Node) bool) bool {
	if n == nil {
		return false
	}
	var do func(Node) bool
	do = func(x Node) bool {
		return cond(x) || DoChildren(x, do)
	}
	return do(n)
}

// AnyList按顺序为列表中的每个节点x调用Any（x，cond）。
// 如果任何调用返回true，AnyList将停止并返回true。
// 否则，AnyList在调用Any（x，cond）后返回false
// 对于列表中的每个x。
func AnyList(list Nodes, cond func(Node) bool) bool {
	for _, x := range list {
		if Any(x, cond) {
			return true
		}
	}
	return false
}

// EditChildren编辑n的子节点，将每个子节点x替换为edit（x）。
// None
// 请注意，EditChildren（n，edit）仅为n的直接子级调用edit（x）。
// 如果应该处理x的子项，则edit（x）必须调用EditChildren（x，edit）。
// None
// EditChildren允许构造IR图形的常规编辑过程。
// 最普遍的用法是：
// None
// 变量编辑函数（ir.Node）ir.Node
// 编辑=函数（x ir.Node）ir.Node{
// ... 在编辑子项之前正在处理。。。
// 如果应该编辑子项。。。{
// 编辑子对象（x，编辑）
// ... 编辑子项后正在处理。。。
// }
// ... 返回x。。。
// }
// n=编辑（n）
// None
// EditChildren在位编辑节点。要编辑副本，请先调用copy。
// 例如，一个简单的深度复制实现是：
// None
// func deepCopy（n ir.Node）ir.Node{
// 变量编辑函数（ir.Node）ir.Node
// 编辑=函数（x ir.Node）ir.Node{
// x=红外复印件（x）
// ir.EditChildren（x，编辑）
// 返回x
// }
// 返回编辑（n）
// }
// None
// 当然，在这种情况下，调用ir.DeepCopy比重新构建一个更好。
func EditChildren(n Node, edit func(Node) Node) {
	if n == nil {
		return
	}
	n.editChildren(edit)
}
